﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Reflection;
using System.Diagnostics;

namespace TorBrowserUpdater
{
    class Program
    {
        static int Main(string[] args)
        {
            try
            {
                try
                {
                    Console.SetBufferSize(Console.BufferWidth, 8000);
                }
                catch
                {
                    //doesn't work with Mono
                }

                var localPath = Path.GetDirectoryName(new Uri(Assembly.GetEntryAssembly().EscapedCodeBase).LocalPath);
                if(localPath != null)
                    Environment.CurrentDirectory = localPath;

                StartupOptions.Language = "en-US";
                StartupOptions.Force32Bit = false;
                StartupOptions.KeepSignatureFile = false;
                StartupOptions.SkipSignatureVerification = false;
                StartupOptions.PublicKeyFile = null;

                if (args.Length == 1 && (args.Any(arg => arg.ToLower() == "-h" || arg.ToLower() == "--help")))
                {
                    //User is asking for help
                    DisplayUsage();
                    return 0;
                }

                ParseCommandLine(args);

                //Checks the newer version available, and downloads it if necessary.  
                //Does nothing if the newer version is already installed.
                var updater = new TBBUpdater(StartupOptions.Language);
                updater.DownloadReleaseIfNewerExists();

                if (TBBUpdater.ErrorOccurred)
                {
                    Console.WriteLine(
                        "An error occurred while looking for or downloading the latest version of Tor Browser Bundle :\r\n{0}",
                        TBBUpdater.Error);
                    Console.WriteLine(
                        "Do you want to start the installed version of Tor Browser Bundle anyway ? (Y/N)");
                    if (Console.ReadKey().Key == ConsoleKey.N)
                    {
                        return 0;
                    }
                }

                if (TBBUpdater.NewVersionDownloaded)
                {
                    UnpackTor(TBBUpdater.DownloadedFilename);
                }
                StartTor();

                if (Debugger.IsAttached)
                    Console.ReadKey();

                return 0;
            }
            catch (Exception ex)
            {
#if DEBUG
                Console.WriteLine("{0}", ex);
#else
                if(StartupOptions.Verbose)
                {
                    Console.WriteLine("{0}", (StartupOptions.Verbose ? ex.ToString() : ex.Message));
                }
#endif
                Console.ReadKey();
                return 1;
            }
        }

        private static void DisplayUsage()
        {
            string assemblyFilename = Path.GetFileName(Assembly.GetExecutingAssembly().GetName().Name);
            string usageText = Properties.Resources.Usage.Replace("{AssemblyFilename}", assemblyFilename);
            Console.WriteLine(usageText);
        }

        static void ParseCommandLine(IList<string> cmdArgs)
        {

            for (int i = 0; i < cmdArgs.Count; i++)
            {
                string currentOption = cmdArgs[i].ToLower();
                switch (currentOption)
                {
                    case "-l": case "--language":

                        if (++i == cmdArgs.Count)
                        {
                            throw new TBBUpdaterException(
                                "Could not parse option " + currentOption + " : Language identifier expected.");
                        }
                        StartupOptions.Language = cmdArgs[i];
                        break;

                    case "-f": case "--force-32bit":

                        StartupOptions.Force32Bit = true;
                        break;

                    case "-p" : case "--public-key" :
                        if(++i == cmdArgs.Count)
                        {
                            throw new TBBUpdaterException(
                                "Could not parse option " + currentOption + ": Public key file path expected.");
                        }
                        StartupOptions.PublicKeyFile =
                            Tools.EnsureFileExists(
                                cmdArgs[i],
                                "Could not find public key file " +
                                cmdArgs[i] + ".");

                        break;

                    case "-s": case "--skip-signature-verification":
                        StartupOptions.SkipSignatureVerification = true;
                        break;

                    case "-k": case "--keep-signature-file":
                        StartupOptions.KeepSignatureFile = true;
                        break;

                    case "-m": case "--mirror":
                        if(++i == cmdArgs.Count)
                        {
                            throw new TBBUpdaterException(
                                "Could not parse option " + currentOption + " : Mirror address expected.");
                        }
                        StartupOptions.Mirror = cmdArgs[i];
                        break;

                    case "-pt": case "--pluggable-transports":
                        StartupOptions.UsePluggableTransports = true;
                        break;

                    case "-v": case "--verbose":
                        StartupOptions.Verbose = true;
                        break;

                    default:
                        throw new TBBUpdaterException(
                            string.Format(
                                "Incorrect command line : parameter {0} unknown.",
                                currentOption));
                }
            }
        }

        private static void UnpackTor(string archiveFilename)
        {
            if (Environment.OSVersion.Platform == PlatformID.Unix)
            {
                //Using the builtin tar command, it reduces dependencies if you are on linux.
                Tools.StartProgram(
                    "tar",
                    "xvf " + archiveFilename);
            }
            else
            {
                string destinationDir = PathHelper.GetUncompressedTBBFolderPath(StartupOptions.Language);
                string args;
                if (StartupOptions.UsePluggableTransports)
                {
                    //this is a 7zip SFX archive
                    args = "-Y -O \"" + destinationDir + "\"";
                }
                else
                {
                    //this is an NSIS installer
                    args = "/S /D=" + destinationDir;
                }
                Tools.StartProgram(archiveFilename, args);                
            }
        }

        private static void StartTor()
        {
            string tbbHomeFolder = PathHelper.GetUncompressedTBBFolderPath(StartupOptions.Language);
            string startTorFilePath = Path.Combine(tbbHomeFolder, PathHelper.TorStarterRelPath);
            string invokeCommand = startTorFilePath.Trim('\"');

            Console.WriteLine();
            Console.WriteLine("Executing program in {0}...", new FileInfo(startTorFilePath).DirectoryName);
            Tools.StartProgram(invokeCommand, "",
                               new FileInfo(startTorFilePath).DirectoryName);
        }
    }
}
